﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;

using iTool.ClusterComponent;

using Microsoft.Data.Sqlite;

using Orleans.Runtime;

namespace iTool.Cloud.DataSearch.ServiceProvider
{
    public class LuceneDirectoryService : iToolServiceStorageBase<List<string>>, ILuceneDirectoryService
    {
        bool isLocal = false;
        List<string> _State;
        List<string> ProxyState
        {
            get
            {
                return isLocal ? _State : State;
            }
            set
            {
                if (isLocal)
                {
                    _State = value;
                }
                else
                {
                    State = value;
                }
            }
        }

        // AUTOINCREMENT 自增
        const string PRIMARY_KEY_KEYWORD = "FIELDNAME";
        const string VALUE_NOT_NULL = "NOT NULL";
        const string PRIMARY_KEY_KEYWORD_TYPE = "VARCHAR PRIMARY KEY";
        const string CREATE_FIELD_SQL = "{0} {1} {2}";

        const string DirectoryPath = "./Storage/Index";
        string DataSource = DirectoryPath + "/{0}.index";
        string connectionString;
        long tableHash;

        public LuceneDirectoryService()
        {
            if (!Directory.Exists(DirectoryPath))
                Directory.CreateDirectory(DirectoryPath);
        }

        public async override Task OnActivateAsync()
        {
            this.tableHash = this.GetLongKey();
            this.connectionString = new SqliteConnectionStringBuilder
            {
                DataSource = String.Format(this.DataSource, this.tableHash),
                Mode = SqliteOpenMode.ReadWriteCreate,
                Cache = SqliteCacheMode.Shared,
                Pooling = true
            }.ToString();

            string sql = @$"CREATE TABLE IF NOT EXISTS FileMetaData({string.Format(CREATE_FIELD_SQL, PRIMARY_KEY_KEYWORD, PRIMARY_KEY_KEYWORD_TYPE, VALUE_NOT_NULL)},IsDelete INT NOT NULL,LASTDATETIME LONG NOT NULL)";
            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                using (SqliteCommand command = new SqliteCommand(sql, connection))
                {
                    command.ExecuteNonQuery();
                }
            }

            await base.OnActivateAsync();
        }

        public async Task AddFileAsync(string name)
        {
            if (!this.ProxyState.Contains(name))
            {
                this.ProxyState.Add(name);
                await this.WriteStateAsync(name);
            }
        }

        public async Task DeleteFileAsync(string name)
        {
            if (this.isLocal)
            {
                await IDirectoryServiceFactory<IndexFieldManageService>.GetService(this.tableHash, name).DeleteFileAsync();
            }
            else
            {
                await this.GetService<IIndexFieldManageService>(this.tableHash, name).DeleteFileAsync();
            }
            await this.ClearStateAsync(name);
            this.ProxyState.Remove(name);
        }

        public Task<bool> FileExistsAsync(string name)
        {
            return Task.FromResult(this.ProxyState.Contains(name));
        }

        public Task<string[]> ListAllAsync()
        {
            return Task.FromResult(this.ProxyState.ToArray());
        }

        private string GetTimeStamp()
        {
            TimeSpan ts = DateTime.Now - new DateTime(2022, 03, 12, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        protected override async Task ClearStateAsync()
        {
            await Task.CompletedTask;
        }

        protected override Task ReadStateAsync()
        {
            this.ProxyState = new List<string>();
            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                var command = new SqliteCommand($"SELECT {PRIMARY_KEY_KEYWORD} FROM FileMetaData WHERE IsDelete=0", connection);
                var reader = command.ExecuteReader();

                if (!reader.HasRows)
                {
                    return Task.CompletedTask;
                }

                while (reader.Read())
                {
                    this.ProxyState.Add(reader.GetString(0));
                }
            }
            return Task.CompletedTask;
        }

        protected async Task WriteStateAsync(string fieldName)
        {
            await using (var connection = new SqliteConnection(this.connectionString))
            {
                await connection.OpenAsync();
                var command = connection.CreateCommand();
                command.CommandText = $"INSERT INTO FileMetaData({PRIMARY_KEY_KEYWORD},IsDelete,LASTDATETIME) VALUES ($key,0,$date);";
                command.Parameters.AddWithValue("$key", fieldName);
                command.Parameters.AddWithValue("$date", this.GetTimeStamp());
                await command.ExecuteNonQueryAsync();
            }
        }

        protected async Task ClearStateAsync(string fieldName)
        {
            await using (var connection = new SqliteConnection(this.connectionString))
            {
                await connection.OpenAsync();
                var command = connection.CreateCommand();
                //command.CommandText = $"UPDATE FileMetaData SET IsDelete = 1,LASTDATETIME = $date WHERE {PRIMARY_KEY_KEYWORD} = $key";
                command.CommandText = $"DELETE FROM FileMetaData WHERE {PRIMARY_KEY_KEYWORD} = $key";
                command.Parameters.AddWithValue("$key", fieldName);
                //command.Parameters.AddWithValue("$date", this.GetTimeStamp());
                await command.ExecuteNonQueryAsync();
            }
        }

        protected override async Task WriteStateAsync()
        {
            await Task.CompletedTask;
        }

        public void OnActivateAsync(long hash, string name)
        {
            this.tableHash = hash;
            this.isLocal = true;
            this.connectionString = new SqliteConnectionStringBuilder
            {
                DataSource = String.Format(this.DataSource, hash),
                Mode = SqliteOpenMode.ReadWriteCreate,
                Cache = SqliteCacheMode.Shared,
                Pooling = true
            }.ToString();

            string sql = @$"CREATE TABLE IF NOT EXISTS FileMetaData({string.Format(CREATE_FIELD_SQL, PRIMARY_KEY_KEYWORD, PRIMARY_KEY_KEYWORD_TYPE, VALUE_NOT_NULL)},IsDelete INT NOT NULL,LASTDATETIME LONG NOT NULL)";
            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                using (SqliteCommand command = new SqliteCommand(sql, connection))
                {
                    command.ExecuteNonQuery();
                }
            }

            base.OnActivateAsync().Wait();
        }

        public Task FlushAsync()
        {
            return Task.CompletedTask;
        }
    }
}
